﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using DistributeTransaction.Utils;
using log4net;

namespace DistributeTransaction.DAL
{
    public abstract class GenericRepository<TEntity> where TEntity : class
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(GenericRepository<TEntity>));

        private bool disposed = false;

        internal DatabaseContext dbContext
        {
            private set;
            get;
        }

        internal DbSet<TEntity> dbSet
        {
            private set;
            get;
        }

        public GenericRepository(DatabaseContext context)
        {
            this.dbContext = context;
            this.dbSet = context.Set<TEntity>();
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    dbContext.Dispose();
                }
            }
            this.disposed = true;
        }

        protected virtual void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual IEnumerable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>,
            IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<TEntity> query = dbSet;

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }

        public virtual TEntity GetByID(object id)
        {
            return dbSet.Find(id);
        }

        protected virtual void Insert(TEntity entity)
        {
            dbSet.Add(entity);
        }

        protected virtual void Delete(object id)
        {
            TEntity entityToDelete = dbSet.Find(id);
            Delete(entityToDelete);
        }

        protected virtual void Delete(TEntity entityToDelete)
        {
            if (dbContext.Entry(entityToDelete).State == EntityState.Detached)
            {
                dbSet.Attach(entityToDelete);
            }
            dbSet.Remove(entityToDelete);
        }

        protected virtual void Update(TEntity entityToUpdate)
        {
            dbSet.Attach(entityToUpdate);
            dbContext.Entry(entityToUpdate).State = EntityState.Modified;
        }

        protected virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
        {
            DbSqlQuery<TEntity> sets = dbSet.SqlQuery(query, parameters);
            return sets == null ? null : sets.ToList();
        }

        protected bool ExecuteSqlCommand(string sql, params SqlParameter[] parameters)
        {
            try
            {
                //context.Database.SqlQuery()
                return DBUtil.RunSQLCmd(
                    sql,
                    dbContext.Database.Connection.ConnectionString,
                    parameters);
            }
            catch (SqlException ex)
            {
                log.Error(ex);
                return false;
            }
        }

        protected bool ExecuteStoreProcedure(string sql, params SqlParameter[] parameters)
        {
            try
            {
                return DBUtil.ExecuteStoreProcedure(
                    sql,
                    dbContext.Database.Connection.ConnectionString,
                    parameters);
            }
            catch (SqlException ex)
            {
                log.Error(ex);
                return false;
            }
        }
    }
}